package labox.innovation.gameserver.model.actor;

import java.util.List;
import java.util.Map;
import javolution.util.FastList;
import javolution.util.FastMap;
import labox.innovation.config.Config;
import labox.innovation.gameserver.ai.CtrlIntention;
import labox.innovation.gameserver.ai.L2AttackableAI;
import labox.innovation.gameserver.model.ItemDropManage;
import labox.innovation.gameserver.model.L2CharPosition;
import labox.innovation.gameserver.model.L2CommandChannel;
import labox.innovation.gameserver.model.L2ItemInstance;
import labox.innovation.gameserver.model.L2Party;
import labox.innovation.gameserver.model.L2Skill;
import labox.innovation.gameserver.model.actor.instance.L2MinionInstance;
import labox.innovation.gameserver.model.actor.instance.FuzeMonsterInstance;
import labox.innovation.gameserver.model.actor.instance.FuzePcInstance;
import labox.innovation.gameserver.model.actor.knownlist.AttackableKnownList;
import labox.innovation.gameserver.model.actor.status.AttackableStatus;
import labox.innovation.gameserver.model.quest.Quest;
import labox.innovation.gameserver.network.clientpackets.Say2;
import labox.innovation.gameserver.network.serverpackets.CreatureSay;
import labox.innovation.gameserver.templates.chars.NpcTemplate;
import labox.innovation.gameserver.timer.Timer;

public class L2Attackable extends L2Npc {

	/**
	 * 仇恨信息对象类，此类包含所有的仇恨信息.
	 * 
	 * Data: attacker : The attacker L2Character concerned by this AggroInfo of this L2Attackable hate : Hate level of this L2Attackable against the attacker L2Character (hate = damage) damage : Number of damages that the attacker L2Character gave to this L2Attackable
	 * 
	 */
	public final class AggroInfo {

		protected L2Character _attacker;

		protected int _hate;

		protected int _damage;

		AggroInfo(L2Character pAttacker) {
			_attacker = pAttacker;
		}

		public boolean equals(Object obj) {
			if (this == obj)
				return true;

			if (obj instanceof AggroInfo)
				return (((AggroInfo) obj)._attacker == _attacker);

			return false;
		}
	}

	private L2CommandChannel _firstCommandChannelAttacked;

	private CommandChannelTimer _commandChannelTimer;

	/**
	 * 仇恨列表
	 */
	private FastMap<L2Character, AggroInfo> _aggroList = new FastMap<L2Character, AggroInfo>();

	/**
	 * Constructor of L2Attackable (use L2Character and L2NpcInstance constructor).
	 * 
	 * Actions: Call the L2Character constructor to set the _template of the L2Attackable (copy skills from template to object and link _calculators to NPC_STD_CALCULATOR) Set the name of the L2Attackable Create a RandomAnimation Task that will be launched after the calculated delay if the server allow it
	 * 
	 * @param objectId
	 *            Identifier of the object to initialized
	 * @param NpcTemplate
	 *            Template to apply to the NPC
	 */
	public L2Attackable(long objectId, NpcTemplate template) {
		super(objectId, template);
		setIsInvul(false);
	}

	@Override
	public AttackableKnownList getKnownList() {
		return (AttackableKnownList) _knownList;
	}

	@Override
	public void initKnownList() {
		setKnownList(new AttackableKnownList(this));
	}

	@Override
	public AttackableStatus getStatus() {
		return (AttackableStatus) super.getStatus();
	}

	@Override
	public void initCharStatus() {
		setStatus(new AttackableStatus(this));
	}

	@Override
	public void initAI() {
		_ai = new L2AttackableAI(new AIAccessor());
	}

	/**
	 * 获得怪物的 精英等级
	 * 
	 * @return
	 */
	public int getChampion() {
		return getTemplate().cham.ordinal();
	}

	/**
	 * Return the L2Character AI of the L2Attackable and if its null create a new one.
	 */
	@Override
	public L2AttackableAI getAI() {
		return (L2AttackableAI) _ai;
	}

	/**
	 * Reduce the current HP of the L2Attackable.
	 * 
	 * @param damage
	 *            The HP decrease value
	 * @param attacker
	 *            The L2Character who attacks
	 */
	@Override
	public void reduceCurrentHp(int damage, L2Character attacker, L2Skill skill) {
		reduceCurrentHp(damage, attacker, true, false, skill);
	}

	/**
	 * Reduce the current HP of the L2Attackable, update its _aggroList and launch the doDie Task if necessary.
	 * 
	 * @param i
	 *            The HP decrease value
	 * @param attacker
	 *            The L2Character who attacks
	 * @param awake
	 *            The awake state (If True : stop sleeping)
	 */
	@Override
	public void reduceCurrentHp(int damage, L2Character attacker, boolean awake, boolean isDOT, L2Skill skill) {
		if (isRaid() && _commandChannelTimer == null) {
			if (attacker != null && attacker.getParty() != null && attacker.getParty().isInCommandChannel() && attacker.getParty().getCommandChannel().meetRaidWarCondition(this)) {
				_firstCommandChannelAttacked = attacker.getParty().getCommandChannel();
				_commandChannelTimer = new CommandChannelTimer(this, attacker.getParty().getCommandChannel());
				new Timer(300000, false, _commandChannelTimer).start();// 5 min
				_firstCommandChannelAttacked.broadcastToChannelMembers(new CreatureSay(0, Say2.PART_CHANNAL, "", "You have looting rights!"));
			}
		}

		if (isEventMob)
			return;

		// 增加伤害值和仇恨值到仇恨列表中
		if (attacker != null)
			addDamage(attacker, damage, skill);

		// If this L2Attackable is a L2MonsterInstance and it has spawned minions, call its minions to battle
		if (this instanceof FuzeMonsterInstance) {//如果是个怪物检查是否有仆从
			FuzeMonsterInstance master = (FuzeMonsterInstance) this;

			if (this instanceof L2MinionInstance) {//如果是仆从
				master = ((L2MinionInstance) this).getLeader();

				if (master != null && !master.isInCombat() && !master.isDead()) {
					master.addDamage(attacker, 1, null);
					master.callMinionsToAssist(attacker);//让头领呼叫全部仆从帮忙
				}
			} else
				master.callMinionsToAssist(attacker);//头领呼叫全部仆从帮忙
		}
		// Reduce the current HP of the L2Attackable and launch the doDie Task if necessary
		super.reduceCurrentHp(damage, attacker, awake, isDOT, skill);
	}

	/**
	 * Kill the L2Attackable (the corpse disappeared after 7 seconds), distribute rewards (EXP, Drops...) and notify Quest Engine.
	 * 
	 * Actions: Distribute Exp and SP rewards to L2PcInstance (including Summon owner) that hit the L2Attackable and to their Party members Notify the Quest Engine of the L2Attackable death if necessary Kill the L2NpcInstance (the corpse disappeared after 7 seconds)
	 * 
	 * Caution: This method DOESN'T GIVE rewards to L2PetInstance
	 * 
	 * @param killer
	 *            The L2Character that has killed the L2Attackable
	 */
	@Override
	public boolean doDie(L2Character killer) {
		if (!super.doDie(killer))
			return false;

		// 清空仇恨列表
		clearAggroList();

		//通知任务引擎，此attackable死亡事件
		try {
			FuzePcInstance player = null;
			if (killer != null)
				player = killer.getActingPlayer();
			if (player != null) {
				if (getTemplate().getEventQuests(Quest.QuestEventType.ON_KILL) != null)
					for (Quest quest : getTemplate().getEventQuests(Quest.QuestEventType.ON_KILL)) {
						quest.notifyKill(this, player, killer instanceof L2Summon);
					}

				//-- 增加组队打怪共享功能
				L2Party party = player.getParty();
				int scenid = player.getScene().get_id();

				if (party != null) {
					FastList<FuzePcInstance> partyMenbers = party.getPartyMembers();

					if (getTemplate().getEventQuests(Quest.QuestEventType.ON_KILL) != null)
						for (Quest quest : getTemplate().getEventQuests(Quest.QuestEventType.ON_KILL)) {
							for (FuzePcInstance parter : partyMenbers) {
								if (1 == parter.isOnline() && parter.getScene().get_id() == scenid) {
									if (player.getObjectId() != parter.getObjectId())
										quest.notifyKill(this, parter, killer instanceof L2Summon);
								}
							}
						}
				}

			}

		} catch (Exception e) {
			_log.error("do die error objectID:" + getObjectId() + " objectName:" + getName(), Config.SERVER_ID, e);
		}
		return true;
	}

	/**
	 * 分发经验，物品等给攻击此对象的角色包括他们的队员和宠物
	 * 
	 * Actions: Get the L2PcInstance owner of the L2SummonInstance (if necessary) and L2Party in progress Calculate the Experience and SP rewards in function of the level difference Add Exp and SP rewards to L2PcInstance (including Summon penalty) and to Party members in the known area of the last attacker
	 * 
	 * Caution : This method DOESN'T GIVE rewards to L2PetInstance
	 * 
	 * @param lastAttacker
	 *            The L2Character that has killed the L2Attackable
	 */
	@Override
	protected void calculateRewards(L2Character lastAttacker) {

		try {

			//改由ItemDropManage去管理
			//			// 管理基础的、任务的、范围型的L2Attackable的掉落
			//			doItemDrop(lastAttacker);
			//
			//			//管理被GM添加的指定时间内的特殊掉落
			//			doEventDrop(lastAttacker);

			if (lastAttacker == null)
				return;
			FuzePcInstance player = lastAttacker.getActingPlayer();
			if (player == null)//如果不是和玩家相关的角色杀死返回
				return;

			ItemDropManage.getInstance().dropItem(lastAttacker, this);

			L2Party attackerParty;

			attackerParty = lastAttacker.getParty();

			//如果不在组对中
			if (attackerParty == null) {
				//个人获得经验值=（怪物经验-怪物经验*min(5,(max(3,玩家等级-怪物等级)-3))*1/5）
				//玩家获得经验=个人获得经验值*(杀怪经验系数+系统经验系数+场景经验系数)+杀怪经验额外值
				int evelDiff = lastAttacker.getLevel() - getLevel();
				int defaultExp = getTemplate().rewardExp;
				int exp = defaultExp - defaultExp * Math.min(5, Math.max(3, evelDiff) - 3) / 5;
				exp = (int) (exp * (player.getStat().getKillMobExpRate() + Config.ALT_GAME_EXPONENT_XP + getScene().getExpRate())) + player.getStat().getKillMobExp();
				lastAttacker.addExp(exp);
			} else {
				List<L2Playable> rewardedMembers = new FastList<L2Playable>();
				List<FuzePcInstance> groupMembers = attackerParty.getPartyMembers();
				//计算可已获得组队经验的成员
				//只有同一场景，同一副本的玩家才能或得组队经验
				for (FuzePcInstance pl : groupMembers) {
					if (pl == null || pl.isDead() || pl.getScene() != lastAttacker.getScene() || pl.getInstanceId() != lastAttacker.getInstanceId())
						continue;
					rewardedMembers.add(pl);
				}
				attackerParty.distributeXpAndSp(getTemplate().rewardExp, rewardedMembers, this, lastAttacker);
			}

		} catch (Exception e) {
			_log.error("calculate Rewards objectID:" + getObjectId() + " objectName:" + getName(), Config.SERVER_ID, e);
		}
	}

	/**
	 * 获得仇恨列表
	 * 
	 * @return
	 */
	public final FastMap<L2Character, AggroInfo> getAggroList() {
		return _aggroList;
	}

	/**
	 * 通知任务引擎收到攻击和伤害
	 * 
	 * @param attacker
	 *            The L2Character that gave damages to this L2Attackable
	 * @param damage
	 *            The number of damages given by the attacker L2Character
	 * 
	 */
	public void addDamage(L2Character attacker, int damage, L2Skill skill) {
		if (!isDead()) {
			try {
				FuzePcInstance player = attacker.getActingPlayer();
				if (player != null) {
					if (getTemplate().getEventQuests(Quest.QuestEventType.ON_ATTACK) != null)
						for (Quest quest : getTemplate().getEventQuests(Quest.QuestEventType.ON_ATTACK))
							quest.notifyAttack(this, player, damage, attacker instanceof L2Summon, skill);
				}
			} catch (Exception e) {
				_log.error("add Damage objectID:" + getObjectId() + " objectName:" + getName(), Config.SERVER_ID, e);
			}
		}
	}

	/**
	 * 把伤害和憎恨加入L2Attackable的仇恨列表.
	 * 
	 * @param attacker
	 *            The L2Character that gave damages to this L2Attackable
	 * @param damage
	 *            The number of damages given by the attacker L2Character
	 * @param aggro
	 *            The hate (=damage) given by the attacker L2Character
	 * 
	 */
	public void addDamageHate(L2Character attacker, int damage, int aggro) {
		if (attacker == null)
			return;
		// 获得攻击者的仇恨信息
		AggroInfo ai = _aggroList.get(attacker);

		//如果没有攻击者的仇恨信息，加入
		if (ai == null) {
			ai = new AggroInfo(attacker);
			_aggroList.put(attacker, ai);
		}

		ai._damage += damage;
		ai._hate += aggro;

		FuzePcInstance targetPlayer = attacker.getActingPlayer();

		//通知任务引擎攻击者进入了仇恨范围
		if (targetPlayer != null && aggro == 0) {
			if (getTemplate().getEventQuests(Quest.QuestEventType.ON_AGGRO_RANGE_ENTER) != null)
				for (Quest quest : getTemplate().getEventQuests(Quest.QuestEventType.ON_AGGRO_RANGE_ENTER))
					quest.notifyAggroRangeEnter(this, targetPlayer, (attacker instanceof L2Summon));
		}
	}

	/**
	 * 降低仇恨值
	 * 
	 * @param target
	 * @param amount
	 */
	public void reduceHate(L2Character target, int amount) {

		if (target == null) {
			int count = 0;

			// 遍历所有仇恨列表中的角色仇恨信息
			for (AggroInfo ai : _aggroList.values()) {
				if (ai == null)
					continue;
				ai._hate -= amount;
				if (ai._hate <= 0)
					count++;
			}

			if (count == _aggroList.size()) {//如果所有的角色的仇恨值都小于等于0则清空仇恨表
				clearAggroList();
				getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
			}
			return;
		}

		AggroInfo ai = _aggroList.get(target);

		if (ai == null)
			return;
		ai._hate -= amount;
		if (ai._hate <= 0) {
			if (getMostHated() == null) {//如果仇恨列表中没有仇恨值大于0的对象，清除仇恨列表
				clearAggroList();
				getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
			}
		}
	}

	/**
	 * 清空仇恨表中某个角色
	 */
	public void stopHating(L2Character target) {
		if (target == null)
			return;
		_aggroList.remove(target);
	}

	/**
	 * 返回在L2Attackable的仇恨列表中最憎恨的角色.
	 */
	public L2Character getMostHated() {
		L2Character mostHated = null;
		int maxHate = 0;

		// Go through the aggroList of the L2Attackable
		L2Character chara;
		AggroInfo ai;
		for (Map.Entry<L2Character, AggroInfo> set : _aggroList.entrySet()) {
			chara = set.getKey();
			ai = set.getValue();
			/**
			 * 仇恨值最大原则的另一重要设定是仇恨列表的删除。 玩家A 如果处于怪物B 的仇恨列表中, 当玩家A 跑出怪物B 的追击范围后, 怪物B 会从自身的仇恨列表内将玩家A 的仇恨信息删除; 玩家A 如 果处于怪物B 的仇恨列表中, 当玩家A 跑出怪物B 所在的场景地图后, 怪物B 会从自身的仇恨列表内将玩家A 的仇恨信息删除; 玩家A 如果处于 怪物B 的仇恨列表中, 如果玩家A 死亡, 怪物B 会从自身的仇恨列表内将玩家A 的仇恨信息删除; 玩家A 如果处于怪物B 的仇恨列表中, 如果 玩家B 因为非正常退出游戏或者直接返回人物选择界面, 那么在玩家A 的登出延迟时间结束并完全退出游戏后, 怪物B 会从自身的仇恨列表内将玩家 A 的仇恨信息删除; 怪物死亡后, 怪物的仇恨列表所有内容会被清空, 不再记录玩家的仇恨值。
			 */

			if (!isInsideRadius(chara, getAggroRange()) || chara.getScene() != getScene() || chara.isDead() || chara.isInvul()) {
				_aggroList.remove(chara);
				continue;
			}

			if (ai._hate >= maxHate) {
				mostHated = ai._attacker;
				maxHate = ai._hate;
			}
		}
		return mostHated;
	}

	/**
	 * 返回某个在仇恨列表中的仇恨值
	 * 
	 * @param target
	 *            The L2Character whose hate level must be returned
	 */
	public int getHating(L2Character target) {
		AggroInfo ai = _aggroList.get(target);
		if (ai == null)
			return 0;
		return ai._hate;
	}

	/**
	 * Return the active weapon of this L2Attackable (= null).
	 */
	public L2ItemInstance getActiveWeapon() {
		return null;
	}

	/**
	 * Return True if the _aggroList of this L2Attackable is Empty.
	 */
	public boolean noTarget() {
		return _aggroList.isEmpty();
	}

	/**
	 * Return True if the _aggroList of this L2Attackable contains the L2Character.
	 * 
	 * @param player
	 *            The L2Character searched in the _aggroList of the L2Attackable
	 */
	public boolean containsTarget(L2Character player) {
		return _aggroList.containsKey(player);
	}

	/**
	 * Clear the _aggroList of the L2Attackable.
	 */
	public void clearAggroList() {
		_aggroList.clear();
	}

	@Override
	public void onSpawn() {
		super.onSpawn();
		// check the region where this mob is, do not activate the AI if region is inactive.
		if (isInActiveRegion()) {
			disableCoreAI(false);
			getStatus().stopRegeneration();
		} else
			disableCoreAI(true);
	}

	protected void setCommandChannelTimer(CommandChannelTimer commandChannelTimer) {
		_commandChannelTimer = commandChannelTimer;
	}

	public CommandChannelTimer getCommandChannelTimer() {
		return _commandChannelTimer;
	}

	public L2CommandChannel getFirstCommandChannelAttacked() {
		return _firstCommandChannelAttacked;
	}

	public void setFirstCommandChannelAttacked(L2CommandChannel firstCommandChannelAttacked) {
		_firstCommandChannelAttacked = firstCommandChannelAttacked;
	}

	private class CommandChannelTimer implements Runnable {
		private L2Attackable _monster;

		private L2CommandChannel _channel;

		public CommandChannelTimer(L2Attackable monster, L2CommandChannel channel) {
			_monster = monster;
			_channel = channel;
		}

		/**
		 * @see java.lang.Runnable#run()
		 */
		public void run() {
			_monster.setCommandChannelTimer(null);
			_monster.setFirstCommandChannelAttacked(null);
			for (L2Character player : _monster.getAggroList().keySet()) {
				if (player.isInParty() && player.getParty().isInCommandChannel()) {
					if (player.getParty().getCommandChannel() == _channel) {
						// if a player which is in first attacked CommandChannel, restart the timer ;)
						_monster.setCommandChannelTimer(this);
						_monster.setFirstCommandChannelAttacked(_channel);
						new Timer(300000, false, this).start(); // 5 min
						break;
					}
				}
			}
		}
	}

	/**
	 * 回到出生点的逻辑
	 * 
	 */
	public void returnHome() {
		clearAggroList();
		if (hasAI() && getSpawn() != null)
			getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(getSpawn().getLocx(), getSpawn().getLocy()));
	}
}
